package com.like;


import com.like.view.ResourceTable;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.StackLayout;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.element.PixelMapElement;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.PixelMap;

import java.io.IOException;
import java.util.List;


public class LikeButton extends StackLayout implements Component.ClickedListener {

    private Image icon;
    private DotsView dotsView;
    private CircleView circleView;
    private Icon currentIcon;
    private OnLikeListener likeListener;
    private OnAnimationEndListener animationEndListener;
    private int circleStartColor;
    private int circleEndColor;
    private int iconSize;


    private float animationScaleFactor;

    private boolean isChecked;


    private boolean isEnabled;
    private AnimatorGroup animatorSet;

    private PixelMapElement likeDrawable;
    private PixelMapElement unLikeDrawable;

    public LikeButton(Context context) {
        this(context, null);
    }

    public LikeButton(Context context, AttrSet attrs) {
        this(context, attrs, "");
    }

    public LikeButton(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    /**
     * Does all the initial setup of the button such as retrieving all the attributes that were
     * set in xml and inflating the like button's view and initial state.
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    private void init(Context context, AttrSet attrs, String defStyle) {
        setClickedListener(this);
//        setTouchEventListener(this);
        Component root = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_likeview, this, true);
        icon = (Image) root.findComponentById(ResourceTable.Id_icon);
        dotsView = (DotsView) root.findComponentById(ResourceTable.Id_dots);
        circleView = (CircleView) root.findComponentById(ResourceTable.Id_circle);
        if (attrs.getAttr("icon_size").isPresent()) {
            iconSize = attrs.getAttr("icon_size").get().getDimensionValue();
        } else {
            iconSize = (int) Utils.dipToPixels(context, 40);
        }
        if (attrs.getAttr("icon_type").isPresent()) {
            String iconType = attrs.getAttr("icon_type").get().getStringValue();
            if (!iconType.isEmpty()) {
                currentIcon = parseIconType(iconType);
            }
        }
        if (attrs.getAttr("like_drawable").isPresent()) {
            likeDrawable = (PixelMapElement) attrs.getAttr("like_drawable").get().getElement();
            if (likeDrawable != null)
                setLikeDrawable(likeDrawable);
        }
        if (attrs.getAttr("unlike_drawable").isPresent()) {
            unLikeDrawable = (PixelMapElement) attrs.getAttr("unlike_drawable").get().getElement();
            if (unLikeDrawable != null)
                setUnlikeDrawable(unLikeDrawable);
        }

        if (attrs.getAttr("circle_start_color").isPresent()) {
            circleStartColor = attrs.getAttr("circle_start_color").get().getColorValue().getValue();
            circleView.setStartColor(circleStartColor);
        }


        if (attrs.getAttr("circle_end_color").isPresent()) {
            circleEndColor = attrs.getAttr("circle_end_color").get().getColorValue().getValue();
            circleView.setEndColor(circleEndColor);
        }
        int dotPrimaryColor = 0, dotSecondaryColor = 0;
        if (attrs.getAttr("dots_primary_color").isPresent()) {
            dotPrimaryColor = attrs.getAttr("dots_primary_color").get().getColorValue().getValue();
        }
        if (attrs.getAttr("dots_secondary_color").isPresent()) {
            dotSecondaryColor = attrs.getAttr("dots_secondary_color").get().getColorValue().getValue();
        }

        if (dotPrimaryColor != 0 && dotSecondaryColor != 0) {
            dotsView.setColors(dotPrimaryColor, dotSecondaryColor);
        }


        if (likeDrawable == null && unLikeDrawable == null) {
            if (currentIcon != null) {
                setIcon();
            } else {
                setIcon(IconType.Heart);
            }
        }

        if (attrs.getAttr("is_enabled").isPresent()) {
            setEnabled(attrs.getAttr("is_enabled").get().getBoolValue());
        } else {
            setEnabled(true);
        }

        if (attrs.getAttr("liked").isPresent()) {
            setLiked(attrs.getAttr("liked").get().getBoolValue());
        } else {
            setLiked(false);
        }

        if (attrs.getAttr("anim_scale_factor").isPresent()) {
            setAnimationScaleFactor(attrs.getAttr("anim_scale_factor").get().getFloatValue());
        } else {
            setAnimationScaleFactor(3);
        }

    }


    /**
     * This triggers the entire functionality of the button such as icon changes,
     * animations, listeners etc.
     *
     * @param component
     */
    @Override
    public void onClick(Component component) {

        if (!isEnabled)
            return;

        isChecked = !isChecked;

        icon.setImageElement(isChecked ? likeDrawable : unLikeDrawable);

        if (likeListener != null) {
            if (isChecked) {
                likeListener.liked(this);
            } else {
                likeListener.unLiked(this);
            }
        }

        if (animatorSet != null) {
            animatorSet.cancel();
        }

        if (isChecked) {
            icon.createAnimatorProperty().cancel();
            icon.setScaleX(0);
            icon.setScaleY(0);
            circleView.setInnerCircleRadiusProgress(0);
            circleView.setOuterCircleRadiusProgress(0);
            dotsView.setCurrentProgress(0);

            animatorSet = new AnimatorGroup();

            AnimatorValue outerCircleAnimator = new AnimatorValue();
            outerCircleAnimator.setDuration(250);
            outerCircleAnimator.setCurveType(Animator.CurveType.DECELERATE);
            outerCircleAnimator.setValueUpdateListener((animatorValue, v) -> {
                circleView.setOuterCircleRadiusProgress(v);
            });

            AnimatorValue innerCircleAnimator = new AnimatorValue();
            innerCircleAnimator.setDuration(200);
            innerCircleAnimator.setDelay(200);
            innerCircleAnimator.setCurveType(Animator.CurveType.DECELERATE);
            innerCircleAnimator.setValueUpdateListener((animatorValue, v) -> {
                circleView.setInnerCircleRadiusProgress(v);
            });

            AnimatorValue starScaleYAnimator = new AnimatorValue();
            starScaleYAnimator.setDuration(350);
            starScaleYAnimator.setDelay(250);
            starScaleYAnimator.setCurveType(Animator.CurveType.OVERSHOOT);
            starScaleYAnimator.setValueUpdateListener((animatorValue, v) -> {
                icon.setScaleY(v);
            });

            AnimatorValue starScaleXAnimator = new AnimatorValue();
            starScaleXAnimator.setDuration(350);
            starScaleXAnimator.setDelay(250);
            starScaleXAnimator.setCurveType(Animator.CurveType.OVERSHOOT);
            starScaleXAnimator.setValueUpdateListener((animatorValue, v) -> {
                icon.setScaleX(v);
            });

            AnimatorValue dotsAnimator = new AnimatorValue();
            dotsAnimator.setDuration(900);
            dotsAnimator.setDelay(50);
            dotsAnimator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
            dotsAnimator.setValueUpdateListener((animatorValue, v) -> {
                dotsView.setCurrentProgress(v);
            });

            animatorSet.runParallel(
                    outerCircleAnimator,
                    innerCircleAnimator,
                    starScaleYAnimator,
                    starScaleXAnimator,
                    dotsAnimator
            );

            animatorSet.setStateChangedListener(new Animator.StateChangedListener() {
                @Override
                public void onStart(Animator animator) {

                }

                @Override
                public void onStop(Animator animator) {

                }

                @Override
                public void onCancel(Animator animator) {
                    circleView.setInnerCircleRadiusProgress(0);
                    circleView.setOuterCircleRadiusProgress(0);
                    dotsView.setCurrentProgress(0);
                    icon.setScaleX(1);
                    icon.setScaleY(1);
                }

                @Override
                public void onEnd(Animator animator) {
                    if (animationEndListener != null) {
                        animationEndListener.onAnimationEnd(LikeButton.this);
                    }
                }

                @Override
                public void onPause(Animator animator) {

                }

                @Override
                public void onResume(Animator animator) {

                }
            });

            animatorSet.start();
        }
    }



    /**
     * This drawable is shown when the button is a liked state.
     *
     * @param resId
     */
    public void setLikeDrawableRes(int resId) {
        icon.setPixelMap(resId);
        if (iconSize != 0) {
            PixelMap pixelMap = icon.getPixelMap();
            likeDrawable = new PixelMapElement(pixelMap);
            likeDrawable.setBounds(0, 0, iconSize, iconSize);
            icon.setWidth(iconSize);
            icon.setHeight(iconSize);
            icon.setImageElement(likeDrawable);
        }
    }

    /**
     * This drawable is shown when the button is in a liked state.
     *
     * @param likeDrawable
     */
    public void setLikeDrawable(PixelMapElement likeDrawable) {
        if (iconSize != 0) {
            likeDrawable.setBounds(0, 0, iconSize, iconSize);
            icon.setWidth(iconSize);
            icon.setHeight(iconSize);
        }
        this.likeDrawable = likeDrawable;

        if (isChecked) {
            icon.setImageElement(this.likeDrawable);
        }
    }

    /**
     * This drawable will be shown when the button is in on unLiked state.
     *
     * @param resId
     */
    public void setUnlikeDrawableRes(int resId) {
        icon.setPixelMap(resId);
        if (iconSize != 0) {
            PixelMap pixelMap = icon.getPixelMap();
            unLikeDrawable = new PixelMapElement(pixelMap);
            unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
            icon.setWidth(iconSize);
            icon.setHeight(iconSize);
            icon.setImageElement(unLikeDrawable);
        }
    }

    /**
     * This drawable will be shown when the button is in on unLiked state.
     *
     * @param unLikeDrawable
     */
    public void setUnlikeDrawable(PixelMapElement unLikeDrawable) {
        if (iconSize != 0) {
            unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
            icon.setWidth(iconSize);
            icon.setHeight(iconSize);
        }
        this.unLikeDrawable = unLikeDrawable;

        if (!isChecked) {
            icon.setImageElement(this.unLikeDrawable);
        }
    }

    /**
     * Sets one of the three icons that are bundled with the library.
     *
     * @param currentIconType
     */
    public void setIcon(IconType currentIconType) {
        currentIcon = parseIconType(currentIconType);
        setLikeDrawableRes(currentIcon.getOnIconResourceId());
        setUnlikeDrawableRes(currentIcon.getOffIconResourceId());
    }

    public void setIcon() {
        setLikeDrawableRes(currentIcon.getOnIconResourceId());
        setUnlikeDrawableRes(currentIcon.getOffIconResourceId());
    }

    /**
     * Sets the size of the drawable/icon that's being used. The views that generate
     * the like effect are also updated to reflect the size of the icon.
     *
     * @param iconSize
     */

    public void setIconSizeDp(int iconSize) {
        setIconSizePx((int) Utils.dipToPixels(getContext(), (float) iconSize));
    }

    /**
     * Sets the size of the drawable/icon that's being used. The views that generate
     * the like effect are also updated to reflect the size of the icon.
     *
     * @param iconSize
     */
    public void setIconSizePx(int iconSize) {
        this.iconSize = iconSize;
        setEffectsViewSize();
        this.unLikeDrawable.setBounds(0, 0, iconSize, iconSize);
        this.likeDrawable.setBounds(0, 0, iconSize, iconSize);
        icon.setWidth(iconSize);
        icon.setHeight(iconSize);
    }

    /**
     * * Parses the specific icon based on string
     * version of its enum.
     * These icons are bundled with the library and
     * are accessed via objects that contain their
     * resource ids and an enum with their name.
     *
     * @param iconType
     * @return Icon
     */
    private Icon parseIconType(String iconType) {
        List<Icon> icons = Utils.getIcons();

        for (Icon icon : icons) {
            if (icon.getIconType().name().toLowerCase().equals(iconType.toLowerCase())) {
                return icon;
            }
        }

        throw new IllegalArgumentException("Correct icon type not specified.");
    }

    /**
     * Parses the specific icon based on it's type.
     * These icons are bundled with the library and
     * are accessed via objects that contain their
     * resource ids and an enum with their name.
     *
     * @param iconType
     * @return
     */
    private Icon parseIconType(IconType iconType) {
        List<Icon> icons = Utils.getIcons();

        for (Icon icon : icons) {
            if (icon.getIconType().equals(iconType)) {
                return icon;
            }
        }

        throw new IllegalArgumentException("Correct icon type not specified.");
    }

    /**
     * Listener that is triggered once the
     * button is in a liked or unLiked state
     *
     * @param likeListener
     */
    public void setOnLikeListener(OnLikeListener likeListener) {
        this.likeListener = likeListener;
    }

    /**
     * Listener that is triggered once the
     * button animation is completed
     *
     * @param animationEndListener
     */
    public void setOnAnimationEndListener(OnAnimationEndListener animationEndListener) {
        this.animationEndListener = animationEndListener;
    }


    /**
     * This set sets the colours that are used for the little dots
     * that will be exploding once the like button is clicked.
     *
     * @param primaryColor
     * @param secondaryColor
     */
    public void setExplodingDotColorsRes(int primaryColor, int secondaryColor) {
        try {
            dotsView.setColors(getResourceManager().getElement(primaryColor).getColor(), getResourceManager().getElement(secondaryColor).getColor());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        }
    }

    public void setExplodingDotColorsInt(int primaryColor, int secondaryColor) {
        dotsView.setColors(primaryColor, secondaryColor);
    }

    public void setCircleStartColorRes(int circleStartColor) {
        try {
            this.circleStartColor = getResourceManager().getElement(circleStartColor).getColor();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        }
        circleView.setStartColor(this.circleStartColor);
    }

    public void setCircleStartColorInt(int circleStartColor) {
        this.circleStartColor = circleStartColor;
        circleView.setStartColor(circleStartColor);
    }

    public void setCircleEndColorRes(int circleEndColor) {
        try {
            this.circleEndColor = getResourceManager().getElement(circleEndColor).getColor();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        }
        circleView.setEndColor(this.circleEndColor);
    }

    /**
     * This function updates the dots view and the circle
     * view with the respective sizes based on the size
     * of the icon being used.
     */
    private void setEffectsViewSize() {
        if (iconSize != 0) {
            dotsView.setSize((int) (iconSize * animationScaleFactor), (int) (iconSize * animationScaleFactor));
            circleView.setSize(iconSize, iconSize);
        }
    }

    /**
     * Sets the initial state of the button to liked
     * or unliked.
     *
     * @param status
     */
    public void setLiked(Boolean status) {
        if (status) {
            isChecked = true;
            icon.setImageElement(likeDrawable);
        } else {
            isChecked = false;
            icon.setImageElement(unLikeDrawable);
        }
    }

    /**
     * Returns current like state
     *
     * @return current like state
     */
    public boolean isLiked() {
        return isChecked;
    }

    @Override
    public void setEnabled(boolean enabled) {
        isEnabled = enabled;
    }

    /**
     * Sets the factor by which the dots should be sized.
     */
    public void setAnimationScaleFactor(float animationScaleFactor) {
        this.animationScaleFactor = animationScaleFactor;

        setEffectsViewSize();
    }


}
